Step 2: Evaluate output
include_graphics("prsice_images/TERRE_PRSice_BARPLOT_2022-06-30.png")

include_graphics("prsice_images/TERRE_PRSice_HIGH-RES_PLOT_2022-06-30.png")

include_graphics("prsice_images/TERRE_PRSice_QUANTILES_PLOT_2022-06-30.png")

include_graphics("prsice_images/TERRE_PRSice_nalls_male_BARPLOT_2022-10-04.png")

include_graphics("prsice_images/TERRE_PRSice_nalls_male_HIGH-RES_PLOT_2022-10-04.png")

include_graphics("prsice_images/TERRE_PRSice_nalls_male_QUANTILES_PLOT_2022-10-04.png")

include_graphics("prsice_images/TERRE_PRSice_nalls_female_BARPLOT_2022-10-04.png")

include_graphics("prsice_images/TERRE_PRSice_nalls_female_HIGH-RES_PLOT_2022-10-04.png")

include_graphics("prsice_images/TERRE_PRSice_nalls_female_QUANTILES_PLOT_2022-10-04.png")

Plotting PRSice Data on my own
library(ggnewscale)
prsice_male_meta <- fread("prsice_nalls_male_data/TERRE_PRSice_nalls_male.prsice")
ggplot(prsice_male_meta[Threshold <= 0.5], aes(Threshold, Num_SNP, color = -log10(P))) +
geom_point() +
scale_y_continuous(breaks = c(seq(0, 1e5, 2.5e4), seq(2e5, 6e5, 1e5))) +
theme_minimal()

prsice_female_meta <- fread("prsice_nalls_female_data/TERRE_PRSice_nalls_female.prsice")
ggplot(prsice_female_meta[Threshold <= 0.5], aes(Threshold, Num_SNP, color = -log10(P))) +
geom_point() +
scale_y_continuous(breaks = c(seq(0, 1e5, 2.5e4), seq(2e5, 6e5, 1e5))) +
theme_minimal()

prsice_meta <- fread("prsice_data/TERRE_PRSice.prsice")
ggplot(mapping = aes(Threshold, R2, color = -log10(P))) +
geom_point(data = prsice_female_meta, size = 1) +
scale_color_gradient(low = "lightpink4", high = "lightpink") +
labs(color = bquote("Female log"["10"] ~ "(P)")) +
new_scale_color() +
geom_point(data = prsice_male_meta, size = 1, aes(color = -log10(P))) +
scale_color_gradient(low = "lightblue4", high = "lightblue") +
labs(y = bquote("R"^2), x = "GWAS P-Value Threshold", color = bquote("Male -log"["10"] ~ "(P)")) +
theme_minimal() +
theme(legend.position = "top")

ggplot(mapping = aes(Threshold, R2, color = -log10(P))) +
geom_point(data = prsice_female_meta, size = 1) +
scale_color_gradient(low = "lightpink4", high = "lightpink",guide = guide_colorbar(order=3)) +
labs(color = bquote("Female -log"["10"] ~ "(P)")) +
new_scale_color() +
geom_point(data = prsice_male_meta, size = 1, aes(color = -log10(P))) +
scale_color_gradient(low = "lightblue4", high = "lightblue",guide = guide_colorbar(order=2)) +
labs(color = bquote("Male -log"["10"] ~ "(P)")) +
new_scale_color() +
geom_point(data = prsice_meta, size = 1, aes(color = -log10(P))) +
scale_color_gradient(low = "gray40", high = "gray80",guide = guide_colorbar(order=1)) +
labs(y = bquote("R"^2), x = "GWAS P-Value Threshold", color = bquote("Cross-sex -log"["10"] ~ "(P)")) +
theme_minimal() +
theme(legend.position = "top",legend.title = element_text(size=7))

Step 3 run linear model at different thresholds for SNP inclusion
This is updated to recently processed DNAm Data 20 outliers in DNAm removed:
load("/home1/NEURO/SHARE_DECIPHER/processed_DNAm_data/2022/TERRE_processed_2022/1-TERRE_RG_filtered.RData") #PD_RG_filtered
# Assign genotyping ID to data
original_covars <- fread("/home1/NEURO/SHARE_DECIPHER/terre_meta_master.csv")[, .(patient, IID = gsub("_PAE.*", "", IID))]
betas_combat <- minfi::getBeta(PD_RG_filtered)
colnames(betas_combat) <- original_covars$IID[match(colnames(betas_combat), original_covars$patient)]
betas_combat <- betas_combat[, colnames(betas_combat) %in% covariate$IID]
Let’s check how the data looks for the first 5 subjects:
ggplot(betas_combat[, 1:5] %>% as.data.table(keep.rownames = T) %>% melt(id.vars = "rn", value.name = "betas", variable.name = "subject"), aes(betas, color = subject))+
geom_density()

Match DNA, PRS, and metadata @TODO fix this after update of PRS etc.
prsice_best <- fread("prsice_data/TERRE_PRSice.best")[match(colnames(betas_combat), IID,nomatch=0),.(best=PRS)]
prsice_all <- cbind(
fread("prsice_data/TERRE_PRSice.all_score")[match(colnames(betas_combat), IID,nomatch=0), .(FID, IID, `Pt_5e-08`, `Pt_5.005e-05`, `Pt_0.00010005`, `Pt_0.00100005`, `Pt_0.0101501`, `Pt_0.1`, `Pt_0.2`, `Pt_0.3`, `Pt_0.4`, `Pt_0.5`, `Pt_1`)],
prsice_best
)
covariate <- covariate[match(colnames(betas_combat), IID)]
all(covariate$IID == colnames(betas_combat))
[1] TRUE
all(covariate$IID == prsice_all$IID)
[1] TRUE
covariate_male <- covariate[sex == 1] %>% select(-sex)
betas_male <- betas_combat[, covariate_male$IID]
prsice_male_best <- fread("prsice_nalls_male_data/TERRE_PRSice_nalls_male.best")[match(colnames(betas_male), IID,nomatch=0),.(best=PRS)]
prsice_male_all <- cbind(
fread("prsice_nalls_male_data/TERRE_PRSice_nalls_male.all_score")[match(colnames(betas_male), IID,nomatch=0), .(FID, IID, `Pt_5e-08`, `Pt_5.005e-05`, `Pt_0.00010005`, `Pt_0.00100005`, `Pt_0.0101501`, `Pt_0.1`, `Pt_0.2`, `Pt_0.3`, `Pt_0.4`, `Pt_0.5`, `Pt_1`)],
prsice_male_best
)
covariate_male <- covariate_male[match(colnames(betas_male), IID,nomatch=0)]
all(covariate_male$IID == colnames(betas_male))
[1] TRUE
all(covariate_male$IID == prsice_male_all$IID)
[1] TRUE
prsice_female_best <- fread("prsice_nalls_female_data/TERRE_PRSice_nalls_female.best")
covariate_female <- covariate[sex == 0] %>% select(-sex) %>% filter(IID %in% prsice_female_best$IID)
betas_female <- betas_combat[, covariate_female$IID]
prsice_female_best <- prsice_female_best[match(colnames(betas_female), IID,nomatch=0),.(best=PRS)]
prsice_female_all <- cbind(
fread("prsice_nalls_female_data/TERRE_PRSice_nalls_female.all_score")[match(colnames(betas_female), IID,nomatch=0), .(FID, IID, `Pt_5e-08`, `Pt_5.005e-05`, `Pt_0.00010005`, `Pt_0.00100005`, `Pt_0.0101501`, `Pt_0.1`, `Pt_0.2`, `Pt_0.3`, `Pt_0.4`, `Pt_0.5`, `Pt_1`)],
prsice_female_best
)
covariate_female <- covariate_female[match(colnames(betas_female), IID,nomatch=0)]
all(covariate_female$IID == colnames(betas_female))
[1] TRUE
all(covariate_female$IID == prsice_female_all$IID)
[1] TRUE
Run limma
mvalues <- lumi::beta2m(betas_combat)
prs_mat <- prsice_all[, -c(1, 2)]
cov_mat <- covariate[, -c(1, 2)]
mvalues_male <- lumi::beta2m(betas_male)
prs_mat_male <- prsice_male_all[, -c(1, 2)]
cov_mat_male <- covariate_male[, -c(1, 2)]
mvalues_female <- lumi::beta2m(betas_female)
prs_mat_female <- prsice_female_all[, -c(1, 2)]
cov_mat_female <- covariate_female[, -c(1, 2)]
registerDoParallel(ncol(prs_mat) / 4)
hits <- foreach(prs_thresh = colnames(prs_mat)) %dopar% {
design_prs <- model.matrix(~., data = cbind(prs_mat[, ..prs_thresh], cov_mat))
prs_fit <- lmFit(mvalues, design_prs)
prs_fit <- eBayes(prs_fit)
topTable(prs_fit, coef = 2, adjust.method = "bonf", p.value = 0.05, number = Inf, genelist = rownames(mvalues))
}
Plotting EWAS vs Threshold Experiment by Sex
to_plot <- rbind(
hits_by_thresh_bonf[, .(hits = .N, Sex = "Cross-sex"), by = threshold] %>%
mutate(threshold = recode_factor(threshold, `Pt_0.0219001` = "0.0219", `Pt_5e-08` = "5e-8", `Pt_5.005e-05` = "5e-5", `Pt_0.00010005` = "1e-4", `Pt_0.00100005` = "1e-3", `Pt_0.0101501` = "1e-2", `Pt_0.1` = "0.1", `Pt_0.2` = "0.2", `Pt_0.3` = "0.3", `Pt_0.4` = "0.4", `Pt_0.5` = "0.5", `Pt_1` = "1.0")),
hits_by_thresh_bonf_male[, .(hits = .N, Sex = "Male"), by = threshold] %>%
mutate(threshold = recode_factor(threshold, `Pt_0.0219001` = "0.0219", `Pt_5e-08` = "5e-8", `Pt_5.005e-05` = "5e-5", `Pt_0.00010005` = "1e-4", `Pt_0.00100005` = "1e-3", `Pt_0.0101501` = "1e-2", `Pt_0.1` = "0.1", `Pt_0.2` = "0.2", `Pt_0.3` = "0.3", `Pt_0.4` = "0.4", `Pt_0.5` = "0.5", `Pt_1` = "1.0")),
hits_by_thresh_bonf_female[, .(hits = .N, Sex = "Female"), by = threshold] %>%
mutate(threshold = recode_factor(threshold, `Pt_0.0219001` = "0.0219", `Pt_5e-08` = "5e-8", `Pt_5.005e-05` = "5e-5", `Pt_0.00010005` = "1e-4", `Pt_0.00100005` = "1e-3", `Pt_0.0101501` = "1e-2", `Pt_0.1` = "0.1", `Pt_0.2` = "0.2", `Pt_0.3` = "0.3", `Pt_0.4` = "0.4", `Pt_0.5` = "0.5", `Pt_1` = "1.0"))
) %>% mutate(Sex = factor(Sex, levels = c("Cross-sex", "Male", "Female")))
plot_pos <- position_dodge(width = 1)
ggplot(to_plot, aes(threshold, hits, fill = Sex, label = hits)) +
geom_text(position = plot_pos, vjust = -0.25) +
geom_col(position = plot_pos) +
labs(x = "GWAS P Value Threshold", y = "EWAS Hits") +
scale_fill_manual(values = c("grey80", "lightblue", "lightpink")) +
theme_minimal()

hits_by_thresh_bonf[, .(hits = .N, Sex = "Cross-sex"), by = threshold]
hits_by_thresh_bonf_male[, .(hits = .N), by = threshold]
hits_by_thresh_bonf_female[, .(hits = .N), by = threshold]
display_venn <- function(x, ...) {
library(VennDiagram)
grid.newpage()
venn_object <- venn.diagram(x, filename = NULL, ...)
grid.draw(venn_object)
}
display_venn(list(`Cross-sex` = hits_by_thresh_bonf[threshold == "Pt_5e-08"]$ID, Male = hits_by_thresh_bonf_male[threshold == "Pt_5e-08"]$ID, Female = hits_by_thresh_bonf_female[threshold == "Pt_5e-08"]$ID), fill = c("gray80", "lightblue", "lightpink"))
Loading required package: grid
Loading required package: futile.logger

male_ids <- hits_by_thresh_bonf_male[threshold == "Pt_5e-08"]$ID
cross_ids <- hits_by_thresh_bonf[threshold == "Pt_5e-08"]$ID
male_ids[!male_ids %in% cross_ids]
character(0)
get_full_fit <- function(prs_mat,cov_mat,mvalues){
top_design_prs <- model.matrix(~., data = cbind(prs_mat[, `Pt_5e-08`], cov_mat))
top_prs_fit <- lmFit(mvalues, top_design_prs)
top_prs_fit <- eBayes(top_prs_fit)
top_prs_hits <- topTable(top_prs_fit, coef = 2, adjust.method = "bonf", number = Inf, genelist = rownames(mvalues))
}
top_prs_hits <- get_full_fit(prs_mat,cov_mat,mvalues)
top_male_prs_hits <- get_full_fit(prs_mat_male, cov_mat_male, mvalues_male)
top_female_prs_hits <- get_full_fit(prs_mat_female, cov_mat_female, mvalues_female)
save(list=c("top_prs_hits","top_male_prs_hits","top_female_prs_hits"),file="prs_nalls_cross_w_sex_stratified.RData")
load("prs_nalls_cross_w_sex_stratified.RData")
manifest <- IlluminaHumanMethylationEPICanno.ilm10b4.hg19::Other %>%
as.data.frame() %>%
rownames_to_column(var = "name")
prs_annot <- data.table(top_prs_hits)[manifest, gene := gsub(";.*", "", UCSC_RefGene_Name), on = c(ID = "name")]
prs_annot_male <- data.table(top_male_prs_hits)[manifest, gene := gsub(";.*", "", UCSC_RefGene_Name), on = c(ID = "name")]
prs_annot_female<- data.table(top_female_prs_hits)[manifest, gene := gsub(";.*", "", UCSC_RefGene_Name), on = c(ID = "name")]
plot_prs_hits <- function(prs_annot,label_color){
ggplot(prs_annot, aes(logFC, -log10(P.Value))) +
geom_point() +
geom_point(
data = subset(prs_annot, adj.P.Val < 0.05 & abs(logFC) > 0.03),
color = label_color,
mapping = aes(logFC, -log10(P.Value))
) +
geom_hline(
linetype = "dashed",
yintercept = min(-log10(prs_annot$P.Value[prs_annot$adj.P.Val < 0.05]))
) +
geom_vline(linetype = "dashed", xintercept = 0.03) +
geom_vline(linetype = "dashed", xintercept = -0.03) +
geom_text_repel(
data = prs_annot %>% filter(abs(logFC) > 0.03 & adj.P.Val < 0.05),
color = "dodgerblue",
mapping = aes(logFC, -log10(P.Value), label = ifelse(gene != "", gene, ID)),
size = 3,
max.overlaps = 20
) +
labs(y = bquote("log"[10] ~ "(P)"), x = quote(Delta ~ "M" ~ Methylation)) +
theme_minimal()
}
plot_prs_hits(prs_annot,"gray40")


plot_prs_hits(prs_annot_male,"lightblue")

plot_prs_hits(prs_annot_female,"pink")


go enrichment?
library(gprofiler2)
cross_sex <- unique(gsub(";.*","",manifest[manifest$name %in% hits_by_thresh_bonf[threshold == "Pt_5e-08"]$ID,]$UCSC_RefGene_Name))
background <- unique(gsub(";.*","",manifest$UCSC_RefGene_Name))
gost_res <- gost(query=cross_sex,custom_bg = background)
gostplot(gost_res)
gmirror(
prs_annot_male[,.(SNP=ID,CHR=gsub("chr","",cpg_pos[match(ID,cpg_pos$name),]$chr),POS=cpg_pos[match(ID,cpg_pos$name),]$pos,pvalue=P.Value)],
prs_annot_female[,.(SNP=ID,CHR=gsub("chr","",cpg_pos[match(ID,cpg_pos$name),]$chr),POS=cpg_pos[match(ID,cpg_pos$name),]$pos,pvalue=P.Value)],
annotate_snp = c(male_annot_cpg,female_annot_cpg),
tline = max(prs_annot_male[adj.P.Val<0.25]$P.Value),
bline = max(prs_annot_female[adj.P.Val<0.25]$P.Value),
highlight_p = c(max(prs_annot_male[adj.P.Val<0.25]$P.Value),max(prs_annot_female[adj.P.Val<0.25]$P.Value)),
toptitle="Male",
bottomtitle ="Female",
highlighter="green",
background="white"
)
[1] "Saving plot to gmirror.png"
TableGrob (2 x 1) "arrange": 2 grobs

Manhattan plot vs GWAS manhattan plot
library(qqman)
copy_annot <- prs_annot[cpg_pos, on = c(ID = "name")] %>%
mutate(chr = as.numeric(recode(gsub("chr", "", chr),X="23",Y="24")))
to_plot <- copy_annot[, .(SNP = gene, CHR = chr, BP = pos, P = P.Value, FDR = adj.P.Val)][!is.na(P)]
manhattan(to_plot,
annotatePval = max(to_plot[FDR < 0.05]$P),
annotateTop = TRUE,
genomewideline = min(-log10(to_plot[FDR < 0.05]$P)),
suggestiveline = FALSE
)
manhattan(to_plot[CHR == 17],
annotatePval = max(to_plot[FDR < 0.05]$P),
annotateTop = FALSE,
genomewideline = min(-log10(to_plot[FDR < 0.05]$P)),
suggestiveline = FALSE
)
qq(to_plot$P)
DMRs
library(DMRcate)
Loading required package: minfi
Loading required package: BiocGenerics
Attaching package: ‘BiocGenerics’
The following objects are masked from ‘package:parallel’:
clusterApply, clusterApplyLB, clusterCall, clusterEvalQ, clusterExport, clusterMap, parApply, parCapply,
parLapply, parLapplyLB, parRapply, parSapply, parSapplyLB
The following object is masked from ‘package:limma’:
plotMA
The following objects are masked from ‘package:dplyr’:
combine, intersect, setdiff, union
The following objects are masked from ‘package:stats’:
IQR, mad, sd, var, xtabs
The following objects are masked from ‘package:base’:
anyDuplicated, append, as.data.frame, basename, cbind, colnames, dirname, do.call, duplicated, eval, evalq,
Filter, Find, get, grep, grepl, intersect, is.unsorted, lapply, Map, mapply, match, mget, order, paste, pmax,
pmax.int, pmin, pmin.int, Position, rank, rbind, Reduce, rownames, sapply, setdiff, sort, table, tapply, union,
unique, unsplit, which, which.max, which.min
Loading required package: GenomicRanges
Loading required package: stats4
Loading required package: S4Vectors
Attaching package: ‘S4Vectors’
The following objects are masked from ‘package:data.table’:
first, second
The following objects are masked from ‘package:dplyr’:
first, rename
The following object is masked from ‘package:tidyr’:
expand
The following object is masked from ‘package:base’:
expand.grid
Loading required package: IRanges
Attaching package: ‘IRanges’
The following object is masked from ‘package:data.table’:
shift
The following objects are masked from ‘package:dplyr’:
collapse, desc, slice
The following object is masked from ‘package:purrr’:
reduce
Loading required package: GenomeInfoDb
Loading required package: SummarizedExperiment
Loading required package: Biobase
Welcome to Bioconductor
Vignettes contain introductory material; view with 'browseVignettes()'. To cite Bioconductor, see
'citation("Biobase")', and for packages 'citation("pkgname")'.
Loading required package: DelayedArray
Loading required package: matrixStats
Attaching package: ‘matrixStats’
The following objects are masked from ‘package:Biobase’:
anyMissing, rowMedians
The following object is masked from ‘package:dplyr’:
count
Loading required package: BiocParallel
Attaching package: ‘DelayedArray’
The following objects are masked from ‘package:matrixStats’:
colMaxs, colMins, colRanges, rowMaxs, rowMins, rowRanges
The following object is masked from ‘package:purrr’:
simplify
The following objects are masked from ‘package:base’:
aperm, apply, rowsum
Loading required package: Biostrings
Loading required package: XVector
Attaching package: ‘XVector’
The following object is masked from ‘package:purrr’:
compact
Attaching package: ‘Biostrings’
The following object is masked from ‘package:base’:
strsplit
Loading required package: bumphunter
Loading required package: locfit
locfit 1.5-9.4 2020-03-24
Attaching package: ‘locfit’
The following object is masked from ‘package:purrr’:
none
replacing previous import 'minfi::getMeth' by 'bsseq::getMeth' when loading 'DMRcate'
S4_to_dataframe <- function(s4obj) {
nms <- slotNames(s4obj)
lst <- lapply(nms, function(nm) slot(s4obj, nm))
as.data.frame(setNames(lst, nms))
}
run_dmrcate <- function(prs_mat,cov_mat,mvalues){
design_prs <- model.matrix(~., data = cbind(prs_mat[, `Pt_5e-08`], cov_mat))
prs_annotated <- cpg.annotate(datatype = "array", object = mvalues, analysis.type = "differential", design = design_prs, coef = 2, what = "M", arraytype = "EPIC", fdr = 0.05)
prs_dmr_res <- dmrcate(prs_annotated, lambda = 1000, C = 2)
return(S4_to_dataframe(prs_dmr_res))
}
dmr_cross <- run_dmrcate(prs_mat, cov_mat, mvalues)
Loading required package: IlluminaHumanMethylationEPICanno.ilm10b4.hg19
Your contrast returned 85 individually significant probes; a small but real effect. Consider manually setting the value of pcutoff to return more DMRs, but be warned that doing this increases the risk of Type I errors.
Fitting chr1...
Fitting chr2...
Fitting chr3...
Fitting chr4...
Fitting chr5...
Fitting chr6...
Fitting chr7...
Fitting chr8...
Fitting chr9...
Fitting chr10...
Fitting chr11...
Fitting chr12...
Fitting chr13...
Fitting chr14...
Fitting chr15...
Fitting chr16...
Fitting chr17...
Fitting chr18...
Fitting chr19...
Fitting chr20...
Fitting chr21...
Fitting chr22...
Fitting chrX...
Fitting chrY...
Demarcating regions...
Done!
dmr_males <- run_dmrcate(prs_mat_male,cov_mat_male,mvalues_male)
Your contrast returned 59 individually significant probes; a small but real effect. Consider manually setting the value of pcutoff to return more DMRs, but be warned that doing this increases the risk of Type I errors.
Fitting chr1...
Fitting chr2...
Fitting chr3...
Fitting chr4...
Fitting chr5...
Fitting chr6...
Fitting chr7...
Fitting chr8...
Fitting chr9...
Fitting chr10...
Fitting chr11...
Fitting chr12...
Fitting chr13...
Fitting chr14...
Fitting chr15...
Fitting chr16...
Fitting chr17...
Fitting chr18...
Fitting chr19...
Fitting chr20...
Fitting chr21...
Fitting chr22...
Fitting chrX...
Fitting chrY...
Demarcating regions...
Done!
dmr_female <- run_dmrcate(prs_mat_female,cov_mat_female,mvalues_female)
Your contrast returned 41 individually significant probes; a small but real effect. Consider manually setting the value of pcutoff to return more DMRs, but be warned that doing this increases the risk of Type I errors.
Fitting chr1...
Fitting chr2...
Fitting chr3...
Fitting chr4...
Fitting chr5...
Fitting chr6...
Fitting chr7...
Fitting chr8...
Fitting chr9...
Fitting chr10...
Fitting chr11...
Fitting chr12...
Fitting chr13...
Fitting chr14...
Fitting chr15...
Fitting chr16...
Fitting chr17...
Fitting chr18...
Fitting chr19...
Fitting chr20...
Fitting chr21...
Fitting chr22...
Fitting chrX...
Fitting chrY...
Demarcating regions...
Done!
annotation <- minfi::getAnnotation(IlluminaHumanMethylationEPICanno.ilm10b4.hg19)
Plotting all DMRs
get_dmr_effects <- function(dmr, limma_res,mvalues) {
dmr_coord <- str_match_all(as.character(dmr), "(chr.*):([0-9]*)-([0-9]*)")[[1]]
cpgs <- as.data.table(annotation[annotation$chr == dmr_coord[2] & annotation$pos >= as.numeric(dmr_coord[3]) & annotation$pos <= as.numeric(dmr_coord[4]), ])
res <- limma_res[cpgs,on=c("ID"="Name"),nomatch=0 ]
dmr_1 <- as.data.frame(res[!is.na(res$logFC), ])
dmr_methy <- reshape2::melt(lumi::m2beta(mvalues[dmr_1$ID, ]), stringsAsFactors = FALSE)
to_plot <- merge(dmr_1, dmr_methy, by.x = "ID", by.y = "Var1")
to_plot$DMR <- dmr
to_plot
}
get_dmr_res <- function(dmr, limma_res) {
dmr_coord <- str_match_all(as.character(dmr), "(chr.*):([0-9]*)-([0-9]*)")[[1]]
cpgs <- annotation[annotation$chr == dmr_coord[2] & annotation$pos >= as.numeric(dmr_coord[3]) & annotation$pos <= as.numeric(dmr_coord[4]), ]
res <- cbind(cpgs, limma_res[cpgs$Name, ])
res$DMR <- dmr
return(res)
}
plot_dmrs <- function(dmr_res,limma_res,prs_mat, mvalues, case_control) {
to_plot <- rbindlist(lapply(dmr_res$coord, function(dmr) get_dmr_effects(dmr, limma_res,mvalues))) %>%
mutate(SCORE1_AVG = scale(prs_mat[match(Var2,IDs$IID),`Pt_5e-08`]),PD = PD[match(Var2,IID)]$PD) %>%
filter(!is.na(SCORE1_AVG))
for(cur_dmr in unique(to_plot$DMR)){
cur_plot <- to_plot[DMR == cur_dmr]
p1 <- ggplot(cur_plot , aes(pos, value, color = SCORE1_AVG)) +
geom_point() +
scale_color_gradient(low = rev(case_control)[1], high = rev(case_control)[2]) +
theme_minimal() +
labs(title = unique(cur_plot$DMR),x="POS",y=bquote("Methylation"~beta),color = "Normalized PD PRS")+
theme(axis.text.x = element_text(angle = 90))
p2 <- ggplot(cur_plot %>% mutate(PD = ifelse(PD == 1, "CASE", "CONTROL")), aes(factor(pos), value, color = PD)) +
geom_boxplot(position = position_dodge(0.75)) +
scale_color_manual(values = case_control) +
theme_minimal() +
stat_summary(aes(group = PD), fun = mean, geom = "line") +
labs(title = unique(cur_plot$DMR),x="POS",y=bquote("Methylation"~beta),color = "PD status") +
theme(axis.text.x = element_text(angle = 90))
print(p1)
print(p2)
}
}
plot_dmrs(dmr_cross, prs_annot, prs_mat,mvalues, rev(c("gray80", "gray40")))




















plot_dmrs(dmr_males,prs_annot_male,prs_mat_male, mvalues_male,rev(c("light blue", "lightblue4")))




















plot_dmrs(dmr_female, prs_annot_female, prs_mat_female, mvalues_female,rev(c("lightpink", "lightpink4")))












dmr_cross
dmr_males
dmr_female
save(list=c("dmr_cross","dmr_males","dmr_female"),file="prs_dmr_nalls.RData")
LS0tCnRpdGxlOiAiUFJTaWNlMiBEZXZlbG9wbWVudCBvZiByaXNrIHNjb3JlIgpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazoKICAgIHRvYzogeWVzCi0tLQoKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KGdncmVwZWwpCmxpYnJhcnkoZGF0YS50YWJsZSkKbGlicmFyeShrbml0cikKbGlicmFyeShsaW1tYSkKbGlicmFyeShmb3JlYWNoKQpsaWJyYXJ5KGRvUGFyYWxsZWwpClN5cy5zZXRsb2NhbGUoIkxDX01FU1NBR0VTIiwgImVuX1VTLnV0ZjgiKQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUpCmBgYAoKIyBTdGVwIDA6IFByZXBhcmUgY292YXJpYXRlcyBhbmQgaW5wdXQgZmlsZXMKYGBge3J9CklEcyA8LSBmcmVhZCgifi9nZW5vdHlwZV9xYy9URVJSRV9RQy9hbGxfaW1wdXRlZF9yMl8zMF9yc2lkX2hhcmRfY2FsbC5mYW0iKVssIC4oRklEID0gVjEsIElJRCA9IFYyKV0KcHJzaWNlX2NvdiA8LSBmcmVhZCgicHJzaWNlX2Nvdl9hbmRfc3RhdHVzX212YWx1ZXMudHh0IikKcHJzaWNlX2NvdiA8LSBwcnNpY2VfY292W21hdGNoKElEcyRJSUQscHJzaWNlX2NvdiRJSUQpXQphbGwoSURzJElJRCA9PXByc2ljZV9jb3YkSUlEKQpjb3ZhcmlhdGUgPC0gY2JpbmQoSURzLCBwcnNpY2VfY292WywtYygxNiwxNywxOCldKQoKUEQgPC0gY2JpbmQoSURzLHByc2ljZV9jb3ZbLDE2XSkKCmhlYWQoY292YXJpYXRlKQpoZWFkKFBEKQpmd3JpdGUobmEub21pdChQRCksICJURVJSRS5waGVubyIsIHNlcCA9ICJcdCIpCmZ3cml0ZShuYS5vbWl0KGNvdmFyaWF0ZSksICJURVJSRS5jb3ZhcmlhdGUiLCBzZXAgPSAiXHQiKQpgYGAKYGBge3J9CmNvdmFyaWF0ZQpgYGAKCgojIFN0ZXAgMTogUnVuIFBSU2ljZS0yIG9uIE5hbGxzIGV0IGFsIDIwMTkgU3Vtc3RhdHMKYGBge2Jhc2gsZXZhbD1GQUxTRX0KUnNjcmlwdCAvaG9tZTEvTkVVUk8vY2FzYXp6YS9QUlNpY2UuUiBcCiAgICAtLXByc2ljZSAvaG9tZTEvTkVVUk8vY2FzYXp6YS9QUlNpY2VfbGludXhcCiAgICAtLWJhc2UgL2hvbWUxL05FVVJPL2Nhc2F6emEvbmFsbHNfUEQuUUMuZ3pcCiAgICAtLWJhc2UtaW5mbyBJTkZPOjAuOCBcCiAgICAtLWJhc2UtbWFmIE1BRjowLjAxIFwKICAgIC0tY292IFRFUlJFLmNvdmFyaWF0ZSBcCiAgICAtLWJpbmFyeS10YXJnZXQgVFwKICAgIC0tYmV0YSAgXAogICAgLS1sZCAvaG9tZTEvTkVVUk8vY2FzYXp6YS8xMDAwR19wbGluay9FVVJfcGhhc2UzICBcCiAgICAtLW91dCBURVJSRV9QUlNpY2UgXAogICAgLXEgNVwKICAgIC0tYWxsLXNjb3JlXAogICAgLS1waGVubyBURVJSRS5waGVubyBcCiAgICAtLXNucCBTTlAgXAogICAgLS1zdGF0IGIgXAogICAgLS1wdmFsdWUgcFwKICAgIC0tdGFyZ2V0IC9ob21lMS9ORVVSTy9jYXNhenphL2dlbm90eXBlX3FjL1RFUlJFX1FDL2FsbF9pbXB1dGVkX3IyXzMwX3JzaWRfaGFyZF9jYWxsIFwKICAgIC0tdGhyZWFkIDMyCmBgYAojIFN0ZXAgMjogRXZhbHVhdGUgb3V0cHV0CmBgYHtyLCBvdXQud2lkdGg9IjQwMHB4In0KaW5jbHVkZV9ncmFwaGljcygicHJzaWNlX2ltYWdlcy9URVJSRV9QUlNpY2VfQkFSUExPVF8yMDIyLTA2LTMwLnBuZyIpCmluY2x1ZGVfZ3JhcGhpY3MoInByc2ljZV9pbWFnZXMvVEVSUkVfUFJTaWNlX0hJR0gtUkVTX1BMT1RfMjAyMi0wNi0zMC5wbmciKQppbmNsdWRlX2dyYXBoaWNzKCJwcnNpY2VfaW1hZ2VzL1RFUlJFX1BSU2ljZV9RVUFOVElMRVNfUExPVF8yMDIyLTA2LTMwLnBuZyIpCgppbmNsdWRlX2dyYXBoaWNzKCJwcnNpY2VfaW1hZ2VzL1RFUlJFX1BSU2ljZV9uYWxsc19tYWxlX0JBUlBMT1RfMjAyMi0xMC0wNC5wbmciKQppbmNsdWRlX2dyYXBoaWNzKCJwcnNpY2VfaW1hZ2VzL1RFUlJFX1BSU2ljZV9uYWxsc19tYWxlX0hJR0gtUkVTX1BMT1RfMjAyMi0xMC0wNC5wbmciKQppbmNsdWRlX2dyYXBoaWNzKCJwcnNpY2VfaW1hZ2VzL1RFUlJFX1BSU2ljZV9uYWxsc19tYWxlX1FVQU5USUxFU19QTE9UXzIwMjItMTAtMDQucG5nIikKCmluY2x1ZGVfZ3JhcGhpY3MoInByc2ljZV9pbWFnZXMvVEVSUkVfUFJTaWNlX25hbGxzX2ZlbWFsZV9CQVJQTE9UXzIwMjItMTAtMDQucG5nIikKaW5jbHVkZV9ncmFwaGljcygicHJzaWNlX2ltYWdlcy9URVJSRV9QUlNpY2VfbmFsbHNfZmVtYWxlX0hJR0gtUkVTX1BMT1RfMjAyMi0xMC0wNC5wbmciKQppbmNsdWRlX2dyYXBoaWNzKCJwcnNpY2VfaW1hZ2VzL1RFUlJFX1BSU2ljZV9uYWxsc19mZW1hbGVfUVVBTlRJTEVTX1BMT1RfMjAyMi0xMC0wNC5wbmciKQpgYGAKIyMgUGxvdHRpbmcgUFJTaWNlIERhdGEgb24gbXkgb3duCmBgYHtyfQpsaWJyYXJ5KGdnbmV3c2NhbGUpCnByc2ljZV9tYWxlX21ldGEgPC0gZnJlYWQoInByc2ljZV9uYWxsc19tYWxlX2RhdGEvVEVSUkVfUFJTaWNlX25hbGxzX21hbGUucHJzaWNlIikKZ2dwbG90KHByc2ljZV9tYWxlX21ldGFbVGhyZXNob2xkIDw9IDAuNV0sIGFlcyhUaHJlc2hvbGQsIE51bV9TTlAsIGNvbG9yID0gLWxvZzEwKFApKSkgKwogIGdlb21fcG9pbnQoKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcyA9IGMoc2VxKDAsIDFlNSwgMi41ZTQpLCBzZXEoMmU1LCA2ZTUsIDFlNSkpKSArCiAgdGhlbWVfbWluaW1hbCgpCgpwcnNpY2VfZmVtYWxlX21ldGEgPC0gZnJlYWQoInByc2ljZV9uYWxsc19mZW1hbGVfZGF0YS9URVJSRV9QUlNpY2VfbmFsbHNfZmVtYWxlLnByc2ljZSIpCmdncGxvdChwcnNpY2VfZmVtYWxlX21ldGFbVGhyZXNob2xkIDw9IDAuNV0sIGFlcyhUaHJlc2hvbGQsIE51bV9TTlAsIGNvbG9yID0gLWxvZzEwKFApKSkgKwogIGdlb21fcG9pbnQoKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcyA9IGMoc2VxKDAsIDFlNSwgMi41ZTQpLCBzZXEoMmU1LCA2ZTUsIDFlNSkpKSArCiAgdGhlbWVfbWluaW1hbCgpCgpwcnNpY2VfbWV0YSA8LSBmcmVhZCgicHJzaWNlX2RhdGEvVEVSUkVfUFJTaWNlLnByc2ljZSIpCmdncGxvdChtYXBwaW5nID0gYWVzKFRocmVzaG9sZCwgUjIsIGNvbG9yID0gLWxvZzEwKFApKSkgKwogIGdlb21fcG9pbnQoZGF0YSA9IHByc2ljZV9mZW1hbGVfbWV0YSwgc2l6ZSA9IDEpICsKICBzY2FsZV9jb2xvcl9ncmFkaWVudChsb3cgPSAibGlnaHRwaW5rNCIsIGhpZ2ggPSAibGlnaHRwaW5rIikgKwogIGxhYnMoY29sb3IgPSBicXVvdGUoIkZlbWFsZSBsb2ciWyIxMCJdIH4gIihQKSIpKSArCiAgbmV3X3NjYWxlX2NvbG9yKCkgKwogIGdlb21fcG9pbnQoZGF0YSA9IHByc2ljZV9tYWxlX21ldGEsIHNpemUgPSAxLCBhZXMoY29sb3IgPSAtbG9nMTAoUCkpKSArCiAgc2NhbGVfY29sb3JfZ3JhZGllbnQobG93ID0gImxpZ2h0Ymx1ZTQiLCBoaWdoID0gImxpZ2h0Ymx1ZSIpICsKICBsYWJzKHkgPSBicXVvdGUoIlIiXjIpLCB4ID0gIkdXQVMgUC1WYWx1ZSBUaHJlc2hvbGQiLCBjb2xvciA9IGJxdW90ZSgiTWFsZSAtbG9nIlsiMTAiXSB+ICIoUCkiKSkgKwogIHRoZW1lX21pbmltYWwoKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInRvcCIpCmdncGxvdChtYXBwaW5nID0gYWVzKFRocmVzaG9sZCwgUjIsIGNvbG9yID0gLWxvZzEwKFApKSkgKwogIGdlb21fcG9pbnQoZGF0YSA9IHByc2ljZV9mZW1hbGVfbWV0YSwgc2l6ZSA9IDEpICsKICBzY2FsZV9jb2xvcl9ncmFkaWVudChsb3cgPSAibGlnaHRwaW5rNCIsIGhpZ2ggPSAibGlnaHRwaW5rIixndWlkZSA9IGd1aWRlX2NvbG9yYmFyKG9yZGVyPTMpKSArCiAgbGFicyhjb2xvciA9IGJxdW90ZSgiRmVtYWxlIC1sb2ciWyIxMCJdIH4gIihQKSIpKSArCiAgbmV3X3NjYWxlX2NvbG9yKCkgKwogIGdlb21fcG9pbnQoZGF0YSA9IHByc2ljZV9tYWxlX21ldGEsIHNpemUgPSAxLCBhZXMoY29sb3IgPSAtbG9nMTAoUCkpKSArCiAgc2NhbGVfY29sb3JfZ3JhZGllbnQobG93ID0gImxpZ2h0Ymx1ZTQiLCBoaWdoID0gImxpZ2h0Ymx1ZSIsZ3VpZGUgPSBndWlkZV9jb2xvcmJhcihvcmRlcj0yKSkgKwogIGxhYnMoY29sb3IgPSBicXVvdGUoIk1hbGUgLWxvZyJbIjEwIl0gfiAiKFApIikpICsKICBuZXdfc2NhbGVfY29sb3IoKSArCiAgZ2VvbV9wb2ludChkYXRhID0gcHJzaWNlX21ldGEsIHNpemUgPSAxLCBhZXMoY29sb3IgPSAtbG9nMTAoUCkpKSArCiAgc2NhbGVfY29sb3JfZ3JhZGllbnQobG93ID0gImdyYXk0MCIsIGhpZ2ggPSAiZ3JheTgwIixndWlkZSA9IGd1aWRlX2NvbG9yYmFyKG9yZGVyPTEpKSArCiAgbGFicyh5ID0gYnF1b3RlKCJSIl4yKSwgeCA9ICJHV0FTIFAtVmFsdWUgVGhyZXNob2xkIiwgY29sb3IgPSBicXVvdGUoIkNyb3NzLXNleCAtbG9nIlsiMTAiXSB+ICIoUCkiKSkgKwogIHRoZW1lX21pbmltYWwoKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInRvcCIsbGVnZW5kLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemU9NykpCmBgYAoKIyBTdGVwIDMgcnVuIGxpbmVhciBtb2RlbCBhdCBkaWZmZXJlbnQgdGhyZXNob2xkcyBmb3IgU05QIGluY2x1c2lvbgpUaGlzIGlzIHVwZGF0ZWQgdG8gcmVjZW50bHkgcHJvY2Vzc2VkIEROQW0gRGF0YSAyMCBvdXRsaWVycyBpbiBETkFtIHJlbW92ZWQ6CmBgYHtyfQpsb2FkKCIvaG9tZTEvTkVVUk8vU0hBUkVfREVDSVBIRVIvcHJvY2Vzc2VkX0ROQW1fZGF0YS8yMDIyL1RFUlJFX3Byb2Nlc3NlZF8yMDIyLzEtVEVSUkVfUkdfZmlsdGVyZWQuUkRhdGEiKSAjUERfUkdfZmlsdGVyZWQKIyBBc3NpZ24gZ2Vub3R5cGluZyBJRCB0byBkYXRhCm9yaWdpbmFsX2NvdmFycyA8LSBmcmVhZCgiL2hvbWUxL05FVVJPL1NIQVJFX0RFQ0lQSEVSL3RlcnJlX21ldGFfbWFzdGVyLmNzdiIpWywgLihwYXRpZW50LCBJSUQgPSBnc3ViKCJfUEFFLioiLCAiIiwgSUlEKSldCmJldGFzX2NvbWJhdCA8LSBtaW5maTo6Z2V0QmV0YShQRF9SR19maWx0ZXJlZCkKY29sbmFtZXMoYmV0YXNfY29tYmF0KSA8LSBvcmlnaW5hbF9jb3ZhcnMkSUlEW21hdGNoKGNvbG5hbWVzKGJldGFzX2NvbWJhdCksIG9yaWdpbmFsX2NvdmFycyRwYXRpZW50KV0KYmV0YXNfY29tYmF0IDwtIGJldGFzX2NvbWJhdFssIGNvbG5hbWVzKGJldGFzX2NvbWJhdCkgJWluJSBjb3ZhcmlhdGUkSUlEXQpgYGAKTGV0J3MgY2hlY2sgaG93IHRoZSBkYXRhIGxvb2tzIGZvciB0aGUgZmlyc3QgNSBzdWJqZWN0czoKYGBge3J9CmdncGxvdChiZXRhc19jb21iYXRbLCAxOjVdICU+JSBhcy5kYXRhLnRhYmxlKGtlZXAucm93bmFtZXMgPSBUKSAlPiUgbWVsdChpZC52YXJzID0gInJuIiwgdmFsdWUubmFtZSA9ICJiZXRhcyIsIHZhcmlhYmxlLm5hbWUgPSAic3ViamVjdCIpLCBhZXMoYmV0YXMsIGNvbG9yID0gc3ViamVjdCkpKwogIGdlb21fZGVuc2l0eSgpCmBgYAoKIyMjIE1hdGNoIEROQSwgUFJTLCBhbmQgbWV0YWRhdGEgQFRPRE8gZml4IHRoaXMgYWZ0ZXIgdXBkYXRlIG9mIFBSUyBldGMuCmBgYHtyfQpwcnNpY2VfYmVzdCA8LSBmcmVhZCgicHJzaWNlX2RhdGEvVEVSUkVfUFJTaWNlLmJlc3QiKVttYXRjaChjb2xuYW1lcyhiZXRhc19jb21iYXQpLCBJSUQsbm9tYXRjaD0wKSwuKGJlc3Q9UFJTKV0KcHJzaWNlX2FsbCA8LSBjYmluZCgKICBmcmVhZCgicHJzaWNlX2RhdGEvVEVSUkVfUFJTaWNlLmFsbF9zY29yZSIpW21hdGNoKGNvbG5hbWVzKGJldGFzX2NvbWJhdCksIElJRCxub21hdGNoPTApLCAuKEZJRCwgSUlELCBgUHRfNWUtMDhgLCBgUHRfNS4wMDVlLTA1YCwgYFB0XzAuMDAwMTAwMDVgLCBgUHRfMC4wMDEwMDAwNWAsIGBQdF8wLjAxMDE1MDFgLCBgUHRfMC4xYCwgYFB0XzAuMmAsIGBQdF8wLjNgLCBgUHRfMC40YCwgYFB0XzAuNWAsIGBQdF8xYCldLAogIHByc2ljZV9iZXN0CikKY292YXJpYXRlIDwtIGNvdmFyaWF0ZVttYXRjaChjb2xuYW1lcyhiZXRhc19jb21iYXQpLCBJSUQpXQphbGwoY292YXJpYXRlJElJRCA9PSBjb2xuYW1lcyhiZXRhc19jb21iYXQpKQphbGwoY292YXJpYXRlJElJRCA9PSBwcnNpY2VfYWxsJElJRCkKCmNvdmFyaWF0ZV9tYWxlIDwtIGNvdmFyaWF0ZVtzZXggPT0gMV0gJT4lIHNlbGVjdCgtc2V4KQpiZXRhc19tYWxlIDwtIGJldGFzX2NvbWJhdFssIGNvdmFyaWF0ZV9tYWxlJElJRF0KcHJzaWNlX21hbGVfYmVzdCA8LSBmcmVhZCgicHJzaWNlX25hbGxzX21hbGVfZGF0YS9URVJSRV9QUlNpY2VfbmFsbHNfbWFsZS5iZXN0IilbbWF0Y2goY29sbmFtZXMoYmV0YXNfbWFsZSksIElJRCxub21hdGNoPTApLC4oYmVzdD1QUlMpXQpwcnNpY2VfbWFsZV9hbGwgPC0gY2JpbmQoCiAgZnJlYWQoInByc2ljZV9uYWxsc19tYWxlX2RhdGEvVEVSUkVfUFJTaWNlX25hbGxzX21hbGUuYWxsX3Njb3JlIilbbWF0Y2goY29sbmFtZXMoYmV0YXNfbWFsZSksIElJRCxub21hdGNoPTApLCAuKEZJRCwgSUlELCBgUHRfNWUtMDhgLCBgUHRfNS4wMDVlLTA1YCwgYFB0XzAuMDAwMTAwMDVgLCBgUHRfMC4wMDEwMDAwNWAsIGBQdF8wLjAxMDE1MDFgLCBgUHRfMC4xYCwgYFB0XzAuMmAsIGBQdF8wLjNgLCBgUHRfMC40YCwgYFB0XzAuNWAsIGBQdF8xYCldLAogIHByc2ljZV9tYWxlX2Jlc3QKKQpjb3ZhcmlhdGVfbWFsZSA8LSBjb3ZhcmlhdGVfbWFsZVttYXRjaChjb2xuYW1lcyhiZXRhc19tYWxlKSwgSUlELG5vbWF0Y2g9MCldCmFsbChjb3ZhcmlhdGVfbWFsZSRJSUQgPT0gY29sbmFtZXMoYmV0YXNfbWFsZSkpCmFsbChjb3ZhcmlhdGVfbWFsZSRJSUQgPT0gcHJzaWNlX21hbGVfYWxsJElJRCkKCnByc2ljZV9mZW1hbGVfYmVzdCA8LSBmcmVhZCgicHJzaWNlX25hbGxzX2ZlbWFsZV9kYXRhL1RFUlJFX1BSU2ljZV9uYWxsc19mZW1hbGUuYmVzdCIpCmNvdmFyaWF0ZV9mZW1hbGUgPC0gY292YXJpYXRlW3NleCA9PSAwXSAlPiUgc2VsZWN0KC1zZXgpICU+JSBmaWx0ZXIoSUlEICVpbiUgcHJzaWNlX2ZlbWFsZV9iZXN0JElJRCkKYmV0YXNfZmVtYWxlIDwtIGJldGFzX2NvbWJhdFssIGNvdmFyaWF0ZV9mZW1hbGUkSUlEXQpwcnNpY2VfZmVtYWxlX2Jlc3QgPC0gcHJzaWNlX2ZlbWFsZV9iZXN0W21hdGNoKGNvbG5hbWVzKGJldGFzX2ZlbWFsZSksIElJRCxub21hdGNoPTApLC4oYmVzdD1QUlMpXQpwcnNpY2VfZmVtYWxlX2FsbCA8LSBjYmluZCgKICBmcmVhZCgicHJzaWNlX25hbGxzX2ZlbWFsZV9kYXRhL1RFUlJFX1BSU2ljZV9uYWxsc19mZW1hbGUuYWxsX3Njb3JlIilbbWF0Y2goY29sbmFtZXMoYmV0YXNfZmVtYWxlKSwgSUlELG5vbWF0Y2g9MCksIC4oRklELCBJSUQsIGBQdF81ZS0wOGAsIGBQdF81LjAwNWUtMDVgLCBgUHRfMC4wMDAxMDAwNWAsIGBQdF8wLjAwMTAwMDA1YCwgYFB0XzAuMDEwMTUwMWAsIGBQdF8wLjFgLCBgUHRfMC4yYCwgYFB0XzAuM2AsIGBQdF8wLjRgLCBgUHRfMC41YCwgYFB0XzFgKV0sCiAgcHJzaWNlX2ZlbWFsZV9iZXN0CikKY292YXJpYXRlX2ZlbWFsZSA8LSBjb3ZhcmlhdGVfZmVtYWxlW21hdGNoKGNvbG5hbWVzKGJldGFzX2ZlbWFsZSksIElJRCxub21hdGNoPTApXQphbGwoY292YXJpYXRlX2ZlbWFsZSRJSUQgPT0gY29sbmFtZXMoYmV0YXNfZmVtYWxlKSkKYWxsKGNvdmFyaWF0ZV9mZW1hbGUkSUlEID09IHByc2ljZV9mZW1hbGVfYWxsJElJRCkKCgpgYGAKIyMjIFJ1biBsaW1tYQoKYGBge3J9Cm12YWx1ZXMgPC0gbHVtaTo6YmV0YTJtKGJldGFzX2NvbWJhdCkKcHJzX21hdCA8LSBwcnNpY2VfYWxsWywgLWMoMSwgMildCmNvdl9tYXQgPC0gY292YXJpYXRlWywgLWMoMSwgMildCgptdmFsdWVzX21hbGUgPC0gbHVtaTo6YmV0YTJtKGJldGFzX21hbGUpCnByc19tYXRfbWFsZSA8LSBwcnNpY2VfbWFsZV9hbGxbLCAtYygxLCAyKV0KY292X21hdF9tYWxlIDwtIGNvdmFyaWF0ZV9tYWxlWywgLWMoMSwgMildCgptdmFsdWVzX2ZlbWFsZSA8LSBsdW1pOjpiZXRhMm0oYmV0YXNfZmVtYWxlKQpwcnNfbWF0X2ZlbWFsZSA8LSBwcnNpY2VfZmVtYWxlX2FsbFssIC1jKDEsIDIpXQpjb3ZfbWF0X2ZlbWFsZSA8LSBjb3ZhcmlhdGVfZmVtYWxlWywgLWMoMSwgMildCmBgYAoKYGBge3J9CnJlZ2lzdGVyRG9QYXJhbGxlbChuY29sKHByc19tYXQpIC8gNCkKaGl0cyA8LSBmb3JlYWNoKHByc190aHJlc2ggPSBjb2xuYW1lcyhwcnNfbWF0KSkgJWRvcGFyJSB7CiAgZGVzaWduX3BycyA8LSBtb2RlbC5tYXRyaXgofi4sIGRhdGEgPSBjYmluZChwcnNfbWF0WywgLi5wcnNfdGhyZXNoXSwgY292X21hdCkpCiAgcHJzX2ZpdCA8LSBsbUZpdChtdmFsdWVzLCBkZXNpZ25fcHJzKQogIHByc19maXQgPC0gZUJheWVzKHByc19maXQpCiAgdG9wVGFibGUocHJzX2ZpdCwgY29lZiA9IDIsIGFkanVzdC5tZXRob2QgPSAiYm9uZiIsIHAudmFsdWUgPSAwLjA1LCBudW1iZXIgPSBJbmYsIGdlbmVsaXN0ID0gcm93bmFtZXMobXZhbHVlcykpCn0KbmFtZXMoaGl0cykgPC0gY29sbmFtZXMocHJzX21hdCkKaGl0c19ieV90aHJlc2hfYm9uZiA8LSByYmluZGxpc3QoaGl0cywgaWRjb2wgPSAidGhyZXNob2xkIiwgZmlsbCA9IFRSVUUpCgpyZWdpc3RlckRvUGFyYWxsZWwobmNvbChwcnNfbWF0X21hbGUpIC8gNCkKaGl0c19tYWxlIDwtIGZvcmVhY2gocHJzX3RocmVzaCA9IGNvbG5hbWVzKHByc19tYXRfbWFsZSkpICVkb3BhciUgewogIGRlc2lnbl9wcnNfbWFsZSA8LSBtb2RlbC5tYXRyaXgofi4sIGRhdGEgPSBjYmluZChwcnNfbWF0X21hbGVbLCAuLnByc190aHJlc2hdLCBjb3ZfbWF0X21hbGUpKQogIHByc19maXRfbWFsZSA8LSBsbUZpdChtdmFsdWVzX21hbGUsIGRlc2lnbl9wcnNfbWFsZSkKICBwcnNfZml0X21hbGUgPC0gZUJheWVzKHByc19maXRfbWFsZSkKICB0b3BUYWJsZShwcnNfZml0X21hbGUsIGNvZWYgPSAyLCBhZGp1c3QubWV0aG9kID0gImJvbmYiLCBwLnZhbHVlID0gMC4wNSwgbnVtYmVyID0gSW5mLCBnZW5lbGlzdCA9IHJvd25hbWVzKG12YWx1ZXNfbWFsZSkpCn0KbmFtZXMoaGl0c19tYWxlKSA8LSBjb2xuYW1lcyhwcnNfbWF0X21hbGUpCmhpdHNfYnlfdGhyZXNoX2JvbmZfbWFsZSA8LSByYmluZGxpc3QoaGl0c19tYWxlLCBpZGNvbCA9ICJ0aHJlc2hvbGQiLCBmaWxsID0gVFJVRSkKCnJlZ2lzdGVyRG9QYXJhbGxlbChuY29sKHByc19tYXRfZmVtYWxlKSAvIDQpCmhpdHNfZmVtYWxlIDwtIGZvcmVhY2gocHJzX3RocmVzaCA9IGNvbG5hbWVzKHByc19tYXRfZmVtYWxlKSkgJWRvcGFyJSB7CiAgZGVzaWduX3Byc19mZW1hbGUgPC0gbW9kZWwubWF0cml4KH4uLCBkYXRhID0gY2JpbmQocHJzX21hdF9mZW1hbGVbLCAuLnByc190aHJlc2hdLCBjb3ZfbWF0X2ZlbWFsZSkpCiAgcHJzX2ZpdF9mZW1hbGUgPC0gbG1GaXQobXZhbHVlc19mZW1hbGUsIGRlc2lnbl9wcnNfZmVtYWxlKQogIHByc19maXRfZmVtYWxlIDwtIGVCYXllcyhwcnNfZml0X2ZlbWFsZSkKICB0b3BUYWJsZShwcnNfZml0X2ZlbWFsZSwgY29lZiA9IDIsIGFkanVzdC5tZXRob2QgPSAiYm9uZiIsIHAudmFsdWUgPSAwLjA1LCBudW1iZXIgPSBJbmYsIGdlbmVsaXN0ID0gcm93bmFtZXMobXZhbHVlc19mZW1hbGUpKQp9Cm5hbWVzKGhpdHNfZmVtYWxlKSA8LSBjb2xuYW1lcyhwcnNfbWF0X2ZlbWFsZSkKaGl0c19ieV90aHJlc2hfYm9uZl9mZW1hbGUgPC0gcmJpbmRsaXN0KGhpdHNfZmVtYWxlLCBpZGNvbCA9ICJ0aHJlc2hvbGQiLCBmaWxsID0gVFJVRSkKCmBgYAoKIyMjIFBsb3R0aW5nIEVXQVMgdnMgVGhyZXNob2xkIEV4cGVyaW1lbnQgYnkgU2V4CmBgYHtyfQp0b19wbG90IDwtIHJiaW5kKAogIGhpdHNfYnlfdGhyZXNoX2JvbmZbLCAuKGhpdHMgPSAuTiwgU2V4ID0gIkNyb3NzLXNleCIpLCBieSA9IHRocmVzaG9sZF0gJT4lCiAgICBtdXRhdGUodGhyZXNob2xkID0gcmVjb2RlX2ZhY3Rvcih0aHJlc2hvbGQsIGBQdF8wLjAyMTkwMDFgID0gIjAuMDIxOSIsIGBQdF81ZS0wOGAgPSAiNWUtOCIsIGBQdF81LjAwNWUtMDVgID0gIjVlLTUiLCBgUHRfMC4wMDAxMDAwNWAgPSAiMWUtNCIsIGBQdF8wLjAwMTAwMDA1YCA9ICIxZS0zIiwgYFB0XzAuMDEwMTUwMWAgPSAiMWUtMiIsIGBQdF8wLjFgID0gIjAuMSIsIGBQdF8wLjJgID0gIjAuMiIsIGBQdF8wLjNgID0gIjAuMyIsIGBQdF8wLjRgID0gIjAuNCIsIGBQdF8wLjVgID0gIjAuNSIsIGBQdF8xYCA9ICIxLjAiKSksCiAgaGl0c19ieV90aHJlc2hfYm9uZl9tYWxlWywgLihoaXRzID0gLk4sIFNleCA9ICJNYWxlIiksIGJ5ID0gdGhyZXNob2xkXSAlPiUKICAgIG11dGF0ZSh0aHJlc2hvbGQgPSByZWNvZGVfZmFjdG9yKHRocmVzaG9sZCwgYFB0XzAuMDIxOTAwMWAgPSAiMC4wMjE5IiwgYFB0XzVlLTA4YCA9ICI1ZS04IiwgYFB0XzUuMDA1ZS0wNWAgPSAiNWUtNSIsIGBQdF8wLjAwMDEwMDA1YCA9ICIxZS00IiwgYFB0XzAuMDAxMDAwMDVgID0gIjFlLTMiLCBgUHRfMC4wMTAxNTAxYCA9ICIxZS0yIiwgYFB0XzAuMWAgPSAiMC4xIiwgYFB0XzAuMmAgPSAiMC4yIiwgYFB0XzAuM2AgPSAiMC4zIiwgYFB0XzAuNGAgPSAiMC40IiwgYFB0XzAuNWAgPSAiMC41IiwgYFB0XzFgID0gIjEuMCIpKSwKICBoaXRzX2J5X3RocmVzaF9ib25mX2ZlbWFsZVssIC4oaGl0cyA9IC5OLCBTZXggPSAiRmVtYWxlIiksIGJ5ID0gdGhyZXNob2xkXSAlPiUKICAgIG11dGF0ZSh0aHJlc2hvbGQgPSByZWNvZGVfZmFjdG9yKHRocmVzaG9sZCwgYFB0XzAuMDIxOTAwMWAgPSAiMC4wMjE5IiwgYFB0XzVlLTA4YCA9ICI1ZS04IiwgYFB0XzUuMDA1ZS0wNWAgPSAiNWUtNSIsIGBQdF8wLjAwMDEwMDA1YCA9ICIxZS00IiwgYFB0XzAuMDAxMDAwMDVgID0gIjFlLTMiLCBgUHRfMC4wMTAxNTAxYCA9ICIxZS0yIiwgYFB0XzAuMWAgPSAiMC4xIiwgYFB0XzAuMmAgPSAiMC4yIiwgYFB0XzAuM2AgPSAiMC4zIiwgYFB0XzAuNGAgPSAiMC40IiwgYFB0XzAuNWAgPSAiMC41IiwgYFB0XzFgID0gIjEuMCIpKQopICU+JSBtdXRhdGUoU2V4ID0gZmFjdG9yKFNleCwgbGV2ZWxzID0gYygiQ3Jvc3Mtc2V4IiwgIk1hbGUiLCAiRmVtYWxlIikpKQpwbG90X3BvcyA8LSBwb3NpdGlvbl9kb2RnZSh3aWR0aCA9IDEpCmdncGxvdCh0b19wbG90LCBhZXModGhyZXNob2xkLCBoaXRzLCBmaWxsID0gU2V4LCBsYWJlbCA9IGhpdHMpKSArCiAgZ2VvbV90ZXh0KHBvc2l0aW9uID0gcGxvdF9wb3MsIHZqdXN0ID0gLTAuMjUpICsKICBnZW9tX2NvbChwb3NpdGlvbiA9IHBsb3RfcG9zKSArCiAgbGFicyh4ID0gIkdXQVMgUCBWYWx1ZSBUaHJlc2hvbGQiLCB5ID0gIkVXQVMgSGl0cyIpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCJncmV5ODAiLCAibGlnaHRibHVlIiwgImxpZ2h0cGluayIpKSArCiAgdGhlbWVfbWluaW1hbCgpCmhpdHNfYnlfdGhyZXNoX2JvbmZbLCAuKGhpdHMgPSAuTiwgU2V4ID0gIkNyb3NzLXNleCIpLCBieSA9IHRocmVzaG9sZF0KaGl0c19ieV90aHJlc2hfYm9uZl9tYWxlWywgLihoaXRzID0gLk4pLCBieSA9IHRocmVzaG9sZF0KaGl0c19ieV90aHJlc2hfYm9uZl9mZW1hbGVbLCAuKGhpdHMgPSAuTiksIGJ5ID0gdGhyZXNob2xkXQpgYGAKYGBge3J9CmRpc3BsYXlfdmVubiA8LSBmdW5jdGlvbih4LCAuLi4pIHsKICBsaWJyYXJ5KFZlbm5EaWFncmFtKQogIGdyaWQubmV3cGFnZSgpCiAgdmVubl9vYmplY3QgPC0gdmVubi5kaWFncmFtKHgsIGZpbGVuYW1lID0gTlVMTCwgLi4uKQogIGdyaWQuZHJhdyh2ZW5uX29iamVjdCkKfQoKZGlzcGxheV92ZW5uKGxpc3QoYENyb3NzLXNleGAgPSBoaXRzX2J5X3RocmVzaF9ib25mW3RocmVzaG9sZCA9PSAiUHRfNWUtMDgiXSRJRCwgTWFsZSA9IGhpdHNfYnlfdGhyZXNoX2JvbmZfbWFsZVt0aHJlc2hvbGQgPT0gIlB0XzVlLTA4Il0kSUQsIEZlbWFsZSA9IGhpdHNfYnlfdGhyZXNoX2JvbmZfZmVtYWxlW3RocmVzaG9sZCA9PSAiUHRfNWUtMDgiXSRJRCksIGZpbGwgPSBjKCJncmF5ODAiLCAibGlnaHRibHVlIiwgImxpZ2h0cGluayIpKQptYWxlX2lkcyA8LSBoaXRzX2J5X3RocmVzaF9ib25mX21hbGVbdGhyZXNob2xkID09ICJQdF81ZS0wOCJdJElECmNyb3NzX2lkcyA8LSBoaXRzX2J5X3RocmVzaF9ib25mW3RocmVzaG9sZCA9PSAiUHRfNWUtMDgiXSRJRAptYWxlX2lkc1shbWFsZV9pZHMgJWluJSBjcm9zc19pZHNdCmBgYAoKYGBge3J9CmdldF9mdWxsX2ZpdCA8LSBmdW5jdGlvbihwcnNfbWF0LGNvdl9tYXQsbXZhbHVlcyl7CiAgdG9wX2Rlc2lnbl9wcnMgPC0gbW9kZWwubWF0cml4KH4uLCBkYXRhID0gY2JpbmQocHJzX21hdFssIGBQdF81ZS0wOGBdLCBjb3ZfbWF0KSkKICB0b3BfcHJzX2ZpdCA8LSBsbUZpdChtdmFsdWVzLCB0b3BfZGVzaWduX3BycykKICB0b3BfcHJzX2ZpdCA8LSBlQmF5ZXModG9wX3Byc19maXQpCiAgdG9wX3Byc19oaXRzIDwtIHRvcFRhYmxlKHRvcF9wcnNfZml0LCBjb2VmID0gMiwgYWRqdXN0Lm1ldGhvZCA9ICJib25mIiwgbnVtYmVyID0gSW5mLCBnZW5lbGlzdCA9IHJvd25hbWVzKG12YWx1ZXMpKQp9CnRvcF9wcnNfaGl0cyA8LSBnZXRfZnVsbF9maXQocHJzX21hdCxjb3ZfbWF0LG12YWx1ZXMpCnRvcF9tYWxlX3Byc19oaXRzIDwtIGdldF9mdWxsX2ZpdChwcnNfbWF0X21hbGUsIGNvdl9tYXRfbWFsZSwgbXZhbHVlc19tYWxlKQp0b3BfZmVtYWxlX3Byc19oaXRzIDwtIGdldF9mdWxsX2ZpdChwcnNfbWF0X2ZlbWFsZSwgY292X21hdF9mZW1hbGUsIG12YWx1ZXNfZmVtYWxlKQpzYXZlKGxpc3Q9YygidG9wX3Byc19oaXRzIiwidG9wX21hbGVfcHJzX2hpdHMiLCJ0b3BfZmVtYWxlX3Byc19oaXRzIiksZmlsZT0icHJzX25hbGxzX2Nyb3NzX3dfc2V4X3N0cmF0aWZpZWQuUkRhdGEiKQpgYGAKCmBgYHtyfQpsb2FkKCJwcnNfbmFsbHNfY3Jvc3Nfd19zZXhfc3RyYXRpZmllZC5SRGF0YSIpCmBgYApgYGB7cn0KbWFuaWZlc3QgPC0gSWxsdW1pbmFIdW1hbk1ldGh5bGF0aW9uRVBJQ2Fubm8uaWxtMTBiNC5oZzE5OjpPdGhlciAlPiUKICBhcy5kYXRhLmZyYW1lKCkgJT4lCiAgcm93bmFtZXNfdG9fY29sdW1uKHZhciA9ICJuYW1lIikKcHJzX2Fubm90IDwtIGRhdGEudGFibGUodG9wX3Byc19oaXRzKVttYW5pZmVzdCwgZ2VuZSA6PSBnc3ViKCI7LioiLCAiIiwgVUNTQ19SZWZHZW5lX05hbWUpLCBvbiA9IGMoSUQgPSAibmFtZSIpXQpwcnNfYW5ub3RfbWFsZSA8LSBkYXRhLnRhYmxlKHRvcF9tYWxlX3Byc19oaXRzKVttYW5pZmVzdCwgZ2VuZSA6PSBnc3ViKCI7LioiLCAiIiwgVUNTQ19SZWZHZW5lX05hbWUpLCBvbiA9IGMoSUQgPSAibmFtZSIpXQpwcnNfYW5ub3RfZmVtYWxlPC0gZGF0YS50YWJsZSh0b3BfZmVtYWxlX3Byc19oaXRzKVttYW5pZmVzdCwgZ2VuZSA6PSBnc3ViKCI7LioiLCAiIiwgVUNTQ19SZWZHZW5lX05hbWUpLCBvbiA9IGMoSUQgPSAibmFtZSIpXQpwbG90X3Byc19oaXRzIDwtIGZ1bmN0aW9uKHByc19hbm5vdCxsYWJlbF9jb2xvcil7CiAgZ2dwbG90KHByc19hbm5vdCwgYWVzKGxvZ0ZDLCAtbG9nMTAoUC5WYWx1ZSkpKSArCiAgICBnZW9tX3BvaW50KCkgKwogICAgZ2VvbV9wb2ludCgKICAgICAgZGF0YSA9IHN1YnNldChwcnNfYW5ub3QsIGFkai5QLlZhbCA8IDAuMDUgJiBhYnMobG9nRkMpID4gMC4wMyksCiAgICAgIGNvbG9yID0gbGFiZWxfY29sb3IsCiAgICAgIG1hcHBpbmcgPSBhZXMobG9nRkMsIC1sb2cxMChQLlZhbHVlKSkKICAgICkgKwogICAgZ2VvbV9obGluZSgKICAgICAgbGluZXR5cGUgPSAiZGFzaGVkIiwKICAgICAgeWludGVyY2VwdCA9IG1pbigtbG9nMTAocHJzX2Fubm90JFAuVmFsdWVbcHJzX2Fubm90JGFkai5QLlZhbCA8IDAuMDVdKSkKICAgICkgKwogICAgZ2VvbV92bGluZShsaW5ldHlwZSA9ICJkYXNoZWQiLCB4aW50ZXJjZXB0ID0gMC4wMykgKwogICAgZ2VvbV92bGluZShsaW5ldHlwZSA9ICJkYXNoZWQiLCB4aW50ZXJjZXB0ID0gLTAuMDMpICsKICAgIGdlb21fdGV4dF9yZXBlbCgKICAgICAgZGF0YSA9IHByc19hbm5vdCAlPiUgZmlsdGVyKGFicyhsb2dGQykgPiAwLjAzICYgYWRqLlAuVmFsIDwgMC4wNSksCiAgICAgIGNvbG9yID0gImRvZGdlcmJsdWUiLAogICAgICBtYXBwaW5nID0gYWVzKGxvZ0ZDLCAtbG9nMTAoUC5WYWx1ZSksIGxhYmVsID0gaWZlbHNlKGdlbmUgIT0gIiIsIGdlbmUsIElEKSksCiAgICAgIHNpemUgPSAzLAogICAgICBtYXgub3ZlcmxhcHMgPSAyMAogICAgKSArCiAgICBsYWJzKHkgPSBicXVvdGUoImxvZyJbMTBdIH4gIihQKSIpLCB4ID0gcXVvdGUoRGVsdGEgfiAiTSIgfiBNZXRoeWxhdGlvbikpICsKICAgIHRoZW1lX21pbmltYWwoKQp9CgpwbG90X3Byc19oaXRzKHByc19hbm5vdCwiZ3JheTQwIikKcGxvdF9wcnNfaGl0cyhwcnNfYW5ub3RfbWFsZSwibGlnaHRibHVlIikKcGxvdF9wcnNfaGl0cyhwcnNfYW5ub3RfZmVtYWxlLCJwaW5rIikKYGBgCgoKYGBge3J9CnByc19wbG90X2RhdGEgPC0gZGF0YS50YWJsZShkbmFtID0gbHVtaTo6bTJiZXRhKG12YWx1ZXNbImNnMTI2MDk3ODUiLF0pLCBwcnMgPSBwcnNfbWF0JGBQdF81ZS0wOGAsUEQ9aWZlbHNlKFBEW21hdGNoKGNvbG5hbWVzKG12YWx1ZXMpLElJRCksXSRQRCA9PSAxLCJDYXNlIiwiQ29udHJvbCIpLFNleD1mYWN0b3IoaWZlbHNlKGNvdl9tYXQkc2V4ID09MSwiTWFsZSIsIkZlbWFsZSIpLGxldmVscz1jKCJNYWxlIiwiRmVtYWxlIikpKQpnZ3Bsb3QocHJzX3Bsb3RfZGF0YSxhZXMocHJzLGRuYW0sY29sb3I9U2V4LHNoYXBlID0gUEQsZ3JvdXA9U2V4KSkgKwogIGdlb21fcG9pbnQoc2l6ZT0zKSsKICBnZW9tX3Ntb290aChtZXRob2Q9ImxtIixzZT1GKSsKICBsYWJzKHk9YnF1b3RlKCJNZXRoeWxhdGlvbiJ+YmV0YSkseD0iUGFya2luc29uJ3MgR1JTIikrCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz1jKCJNYWxlIj0ibGlnaHRibHVlIiwiRmVtYWxlIj0ibGlnaHRwaW5rIikpICsgCiAgdGhlbWVfbWluaW1hbChiYXNlX3NpemU9MjApCmBgYAoKIyMgZ28gZW5yaWNobWVudD8KYGBge3IsZXZhbD1GQUxTRX0KbGlicmFyeShncHJvZmlsZXIyKQpjcm9zc19zZXggPC0gdW5pcXVlKGdzdWIoIjsuKiIsIiIsbWFuaWZlc3RbbWFuaWZlc3QkbmFtZSAlaW4lIGhpdHNfYnlfdGhyZXNoX2JvbmZbdGhyZXNob2xkID09ICJQdF81ZS0wOCJdJElELF0kVUNTQ19SZWZHZW5lX05hbWUpKQpiYWNrZ3JvdW5kIDwtIHVuaXF1ZShnc3ViKCI7LioiLCIiLG1hbmlmZXN0JFVDU0NfUmVmR2VuZV9OYW1lKSkKZ29zdF9yZXMgPC0gZ29zdChxdWVyeT1jcm9zc19zZXgsY3VzdG9tX2JnID0gYmFja2dyb3VuZCkKZ29zdHBsb3QoZ29zdF9yZXMpCmBgYApgYGB7cn0KY3BnX3BvcyA8LSBJbGx1bWluYUh1bWFuTWV0aHlsYXRpb25FUElDYW5uby5pbG0xMGI0LmhnMTk6OkxvY2F0aW9ucyAlPiUKICBhcy5kYXRhLmZyYW1lKCkgJT4lCiAgcm93bmFtZXNfdG9fY29sdW1uKHZhciA9ICJuYW1lIikKbWFsZV9hbm5vdF9jcGcgPC0gKHByc19hbm5vdF9tYWxlICU+JSBmaWx0ZXIoYWRqLlAuVmFsIDwgMC4yNSkgJT4lIGxlZnRfam9pbihjcGdfcG9zLGJ5PWMoIklEIj0ibmFtZSIpKSAlPiUgZ3JvdXBfYnkoY2hyKSAlPiUgdG9wX24oLTEwLFAuVmFsdWUpKSRJRApmZW1hbGVfYW5ub3RfY3BnIDwtIChwcnNfYW5ub3RfZmVtYWxlICU+JSBmaWx0ZXIoYWRqLlAuVmFsIDwgMC4yNSkgJT4lIGxlZnRfam9pbihjcGdfcG9zLGJ5PWMoIklEIj0ibmFtZSIpKSAlPiUgZ3JvdXBfYnkoY2hyKSAlPiUgdG9wX24oLTEwLFAuVmFsdWUpKSRJRApsaWJyYXJ5KGh1ZHNvbikKb3B0aW9ucyhnZ3JlcGVsLm1heC5vdmVybGFwcyA9IEluZikKZ21pcnJvcigKICBwcnNfYW5ub3RfbWFsZVssLihTTlA9SUQsQ0hSPWdzdWIoImNociIsIiIsY3BnX3Bvc1ttYXRjaChJRCxjcGdfcG9zJG5hbWUpLF0kY2hyKSxQT1M9Y3BnX3Bvc1ttYXRjaChJRCxjcGdfcG9zJG5hbWUpLF0kcG9zLHB2YWx1ZT1QLlZhbHVlKV0sCiAgcHJzX2Fubm90X2ZlbWFsZVssLihTTlA9SUQsQ0hSPWdzdWIoImNociIsIiIsY3BnX3Bvc1ttYXRjaChJRCxjcGdfcG9zJG5hbWUpLF0kY2hyKSxQT1M9Y3BnX3Bvc1ttYXRjaChJRCxjcGdfcG9zJG5hbWUpLF0kcG9zLHB2YWx1ZT1QLlZhbHVlKV0sCiAgYW5ub3RhdGVfc25wID0gYyhtYWxlX2Fubm90X2NwZyxmZW1hbGVfYW5ub3RfY3BnKSwKICB0bGluZSA9ICBtYXgocHJzX2Fubm90X21hbGVbYWRqLlAuVmFsPDAuMjVdJFAuVmFsdWUpLAogIGJsaW5lID0gIG1heChwcnNfYW5ub3RfZmVtYWxlW2Fkai5QLlZhbDwwLjI1XSRQLlZhbHVlKSwKICBoaWdobGlnaHRfcCA9IGMobWF4KHByc19hbm5vdF9tYWxlW2Fkai5QLlZhbDwwLjI1XSRQLlZhbHVlKSxtYXgocHJzX2Fubm90X2ZlbWFsZVthZGouUC5WYWw8MC4yNV0kUC5WYWx1ZSkpLAogIHRvcHRpdGxlPSJNYWxlIiwKICBib3R0b210aXRsZSA9IkZlbWFsZSIsCiAgaGlnaGxpZ2h0ZXI9ImdyZWVuIiwKICBiYWNrZ3JvdW5kPSJ3aGl0ZSIKKQoKYGBgCgojIE1hbmhhdHRhbiBwbG90IHZzIEdXQVMgbWFuaGF0dGFuIHBsb3QKYGBge3IsZXZhbD1GQUxTRX0KbGlicmFyeShxcW1hbikKCmNvcHlfYW5ub3QgPC0gcHJzX2Fubm90W2NwZ19wb3MsIG9uID0gYyhJRCA9ICJuYW1lIildICU+JQogIG11dGF0ZShjaHIgPSBhcy5udW1lcmljKHJlY29kZShnc3ViKCJjaHIiLCAiIiwgY2hyKSxYPSIyMyIsWT0iMjQiKSkpCnRvX3Bsb3QgPC0gY29weV9hbm5vdFssIC4oU05QID0gZ2VuZSwgQ0hSID0gY2hyLCBCUCA9IHBvcywgUCA9IFAuVmFsdWUsIEZEUiA9IGFkai5QLlZhbCldWyFpcy5uYShQKV0KbWFuaGF0dGFuKHRvX3Bsb3QsCiAgYW5ub3RhdGVQdmFsID0gbWF4KHRvX3Bsb3RbRkRSIDwgMC4wNV0kUCksCiAgYW5ub3RhdGVUb3AgPSBUUlVFLAogIGdlbm9tZXdpZGVsaW5lID0gbWluKC1sb2cxMCh0b19wbG90W0ZEUiA8IDAuMDVdJFApKSwKICBzdWdnZXN0aXZlbGluZSA9IEZBTFNFCikKbWFuaGF0dGFuKHRvX3Bsb3RbQ0hSID09IDE3XSwKICBhbm5vdGF0ZVB2YWwgPSBtYXgodG9fcGxvdFtGRFIgPCAwLjA1XSRQKSwKICBhbm5vdGF0ZVRvcCA9IEZBTFNFLAogIGdlbm9tZXdpZGVsaW5lID0gbWluKC1sb2cxMCh0b19wbG90W0ZEUiA8IDAuMDVdJFApKSwKICBzdWdnZXN0aXZlbGluZSA9IEZBTFNFCikKcXEodG9fcGxvdCRQKQpgYGAKCgojIyBETVJzCgpgYGB7cn0KbGlicmFyeShETVJjYXRlKQpTNF90b19kYXRhZnJhbWUgPC0gZnVuY3Rpb24oczRvYmopIHsKICBubXMgPC0gc2xvdE5hbWVzKHM0b2JqKQogIGxzdCA8LSBsYXBwbHkobm1zLCBmdW5jdGlvbihubSkgc2xvdChzNG9iaiwgbm0pKQogIGFzLmRhdGEuZnJhbWUoc2V0TmFtZXMobHN0LCBubXMpKQp9CnJ1bl9kbXJjYXRlIDwtIGZ1bmN0aW9uKHByc19tYXQsY292X21hdCxtdmFsdWVzKXsKICBkZXNpZ25fcHJzIDwtIG1vZGVsLm1hdHJpeCh+LiwgZGF0YSA9IGNiaW5kKHByc19tYXRbLCBgUHRfNWUtMDhgXSwgY292X21hdCkpCiAgcHJzX2Fubm90YXRlZCA8LSBjcGcuYW5ub3RhdGUoZGF0YXR5cGUgPSAiYXJyYXkiLCBvYmplY3QgPSBtdmFsdWVzLCBhbmFseXNpcy50eXBlID0gImRpZmZlcmVudGlhbCIsIGRlc2lnbiA9IGRlc2lnbl9wcnMsIGNvZWYgPSAyLCB3aGF0ID0gIk0iLCBhcnJheXR5cGUgPSAiRVBJQyIsIGZkciA9IDAuMDUpCiAgcHJzX2Rtcl9yZXMgPC0gZG1yY2F0ZShwcnNfYW5ub3RhdGVkLCBsYW1iZGEgPSAxMDAwLCBDID0gMikKICByZXR1cm4oUzRfdG9fZGF0YWZyYW1lKHByc19kbXJfcmVzKSkKfQpkbXJfY3Jvc3MgPC0gcnVuX2RtcmNhdGUocHJzX21hdCwgY292X21hdCwgbXZhbHVlcykKZG1yX21hbGVzIDwtIHJ1bl9kbXJjYXRlKHByc19tYXRfbWFsZSxjb3ZfbWF0X21hbGUsbXZhbHVlc19tYWxlKQpkbXJfZmVtYWxlIDwtIHJ1bl9kbXJjYXRlKHByc19tYXRfZmVtYWxlLGNvdl9tYXRfZmVtYWxlLG12YWx1ZXNfZmVtYWxlKQpgYGAKYGBge3J9CmFubm90YXRpb24gPC0gbWluZmk6OmdldEFubm90YXRpb24oSWxsdW1pbmFIdW1hbk1ldGh5bGF0aW9uRVBJQ2Fubm8uaWxtMTBiNC5oZzE5KQpgYGAKIyMjIFBsb3R0aW5nIGFsbCBETVJzIHsudGFic2V0IC50YWJzZXQtZmFkZX0KYGBge3J9CmdldF9kbXJfZWZmZWN0cyA8LSBmdW5jdGlvbihkbXIsIGxpbW1hX3JlcyxtdmFsdWVzKSB7CiAgZG1yX2Nvb3JkIDwtIHN0cl9tYXRjaF9hbGwoYXMuY2hhcmFjdGVyKGRtciksICIoY2hyLiopOihbMC05XSopLShbMC05XSopIilbWzFdXQogIGNwZ3MgPC0gYXMuZGF0YS50YWJsZShhbm5vdGF0aW9uW2Fubm90YXRpb24kY2hyID09IGRtcl9jb29yZFsyXSAmIGFubm90YXRpb24kcG9zID49IGFzLm51bWVyaWMoZG1yX2Nvb3JkWzNdKSAmIGFubm90YXRpb24kcG9zIDw9IGFzLm51bWVyaWMoZG1yX2Nvb3JkWzRdKSwgXSkKICByZXMgPC0gbGltbWFfcmVzW2NwZ3Msb249YygiSUQiPSJOYW1lIiksbm9tYXRjaD0wIF0KICBkbXJfMSA8LSBhcy5kYXRhLmZyYW1lKHJlc1shaXMubmEocmVzJGxvZ0ZDKSwgXSkKICBkbXJfbWV0aHkgPC0gcmVzaGFwZTI6Om1lbHQobHVtaTo6bTJiZXRhKG12YWx1ZXNbZG1yXzEkSUQsIF0pLCBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UpCiAgdG9fcGxvdCA8LSBtZXJnZShkbXJfMSwgZG1yX21ldGh5LCBieS54ID0gIklEIiwgYnkueSA9ICJWYXIxIikKICB0b19wbG90JERNUiA8LSBkbXIKICB0b19wbG90Cn0KZ2V0X2Rtcl9yZXMgPC0gZnVuY3Rpb24oZG1yLCBsaW1tYV9yZXMpIHsKICBkbXJfY29vcmQgPC0gc3RyX21hdGNoX2FsbChhcy5jaGFyYWN0ZXIoZG1yKSwgIihjaHIuKik6KFswLTldKiktKFswLTldKikiKVtbMV1dCiAgY3BncyA8LSBhbm5vdGF0aW9uW2Fubm90YXRpb24kY2hyID09IGRtcl9jb29yZFsyXSAmIGFubm90YXRpb24kcG9zID49IGFzLm51bWVyaWMoZG1yX2Nvb3JkWzNdKSAmIGFubm90YXRpb24kcG9zIDw9IGFzLm51bWVyaWMoZG1yX2Nvb3JkWzRdKSwgXQogIHJlcyA8LSBjYmluZChjcGdzLCBsaW1tYV9yZXNbY3BncyROYW1lLCBdKQogIHJlcyRETVIgPC0gZG1yCiAgcmV0dXJuKHJlcykKfQpwbG90X2RtcnMgPC0gZnVuY3Rpb24oZG1yX3JlcyxsaW1tYV9yZXMscHJzX21hdCwgbXZhbHVlcywgY2FzZV9jb250cm9sKSB7CiAgdG9fcGxvdCA8LSByYmluZGxpc3QobGFwcGx5KGRtcl9yZXMkY29vcmQsIGZ1bmN0aW9uKGRtcikgZ2V0X2Rtcl9lZmZlY3RzKGRtciwgbGltbWFfcmVzLG12YWx1ZXMpKSkgJT4lCiAgICBtdXRhdGUoU0NPUkUxX0FWRyA9IHNjYWxlKHByc19tYXRbbWF0Y2goVmFyMixJRHMkSUlEKSxgUHRfNWUtMDhgXSksUEQgPSBQRFttYXRjaChWYXIyLElJRCldJFBEKSAlPiUKICAgIGZpbHRlcighaXMubmEoU0NPUkUxX0FWRykpCiAgZm9yKGN1cl9kbXIgaW4gdW5pcXVlKHRvX3Bsb3QkRE1SKSl7CiAgICBjdXJfcGxvdCA8LSB0b19wbG90W0RNUiA9PSBjdXJfZG1yXQogICAgcDEgPC0gZ2dwbG90KGN1cl9wbG90ICwgYWVzKHBvcywgdmFsdWUsIGNvbG9yID0gU0NPUkUxX0FWRykpICsKICAgICAgZ2VvbV9wb2ludCgpICsKICAgICAgc2NhbGVfY29sb3JfZ3JhZGllbnQobG93ID0gcmV2KGNhc2VfY29udHJvbClbMV0sIGhpZ2ggPSByZXYoY2FzZV9jb250cm9sKVsyXSkgKwogICAgICB0aGVtZV9taW5pbWFsKCkgKwogICAgICBsYWJzKHRpdGxlID0gdW5pcXVlKGN1cl9wbG90JERNUikseD0iUE9TIix5PWJxdW90ZSgiTWV0aHlsYXRpb24ifmJldGEpLGNvbG9yID0gIk5vcm1hbGl6ZWQgUEQgUFJTIikrCiAgICAgIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTApKQogICAgcDIgPC0gZ2dwbG90KGN1cl9wbG90ICU+JSBtdXRhdGUoUEQgPSBpZmVsc2UoUEQgPT0gMSwgIkNBU0UiLCAiQ09OVFJPTCIpKSwgYWVzKGZhY3Rvcihwb3MpLCB2YWx1ZSwgY29sb3IgPSBQRCkpICsKICAgICAgZ2VvbV9ib3hwbG90KHBvc2l0aW9uID0gcG9zaXRpb25fZG9kZ2UoMC43NSkpICsKICAgICAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGNhc2VfY29udHJvbCkgKwogICAgICB0aGVtZV9taW5pbWFsKCkgKwogICAgICBzdGF0X3N1bW1hcnkoYWVzKGdyb3VwID0gUEQpLCBmdW4gPSBtZWFuLCBnZW9tID0gImxpbmUiKSArCiAgICAgIGxhYnModGl0bGUgPSB1bmlxdWUoY3VyX3Bsb3QkRE1SKSx4PSJQT1MiLHk9YnF1b3RlKCJNZXRoeWxhdGlvbiJ+YmV0YSksY29sb3IgPSAiUEQgc3RhdHVzIikgKwogICAgICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwKSkKICAgIHByaW50KHAxKQogICAgcHJpbnQocDIpCiAgfQp9CgpwbG90X2RtcnMoZG1yX2Nyb3NzLCBwcnNfYW5ub3QsIHByc19tYXQsbXZhbHVlcywgcmV2KGMoImdyYXk4MCIsICJncmF5NDAiKSkpCnBsb3RfZG1ycyhkbXJfbWFsZXMscHJzX2Fubm90X21hbGUscHJzX21hdF9tYWxlLCBtdmFsdWVzX21hbGUscmV2KGMoImxpZ2h0IGJsdWUiLCAibGlnaHRibHVlNCIpKSkKcGxvdF9kbXJzKGRtcl9mZW1hbGUsIHByc19hbm5vdF9mZW1hbGUsIHByc19tYXRfZmVtYWxlLCBtdmFsdWVzX2ZlbWFsZSxyZXYoYygibGlnaHRwaW5rIiwgImxpZ2h0cGluazQiKSkpCmBgYAoKCmBgYHtyfQpkbXJfY3Jvc3MKZG1yX21hbGVzCmRtcl9mZW1hbGUKc2F2ZShsaXN0PWMoImRtcl9jcm9zcyIsImRtcl9tYWxlcyIsImRtcl9mZW1hbGUiKSxmaWxlPSJwcnNfZG1yX25hbGxzLlJEYXRhIikKYGBgCg==